Skip to content

Spring学习_AOP

1、什么是AOP

AOP(Aspect-Oriented Programming:面向切面编程)是一种编程范式,通过在代码中横向插入关注点(如日志、事务),实现对应用程序模块化、可维护性和可重用性的提升。

AOP 切面编程涉及到的一些专业术语:

术语含义
目标(Target)被通知的对象
代理(Proxy)向目标对象应用通知之后创建的代理对象
连接点(JoinPoint)目标对象的所属类中,定义的所有方法均为连接点
切入点(Pointcut)被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)
通知(Advice)增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情
切面(Aspect)切入点(Pointcut)+通知(Advice)
Weaving(织入)将通知应用到目标对象,进而生成代理对象的过程动作

2、Spring AOP 的实现

Spring AOP基于动态代理实现,

  • 对于实现了接口的对象,使用JDK Proxy创建代理对象;
  • 对于未实现接口的对象,使用Cglib生成被代理对象的子类。

Spring AOP还集成了AspectJ,AspectJ是Java生态系统中最完整的AOP框架,拥有更多功能,特别在切面较多的情况下,选择AspectJ相比Spring AOP更为高效。


Spring的AOP(面向切面编程)允许开发者定义横切关注点(如日志、事务管理等),这些关注点通常与业务逻辑的主流程解耦,可以跨越应用的多个点。AOP通过在运行时动态地将代码注入到指定位置来实现这一点,而这正是通过“代理”完成的。

实现AOP的方式主要有两种:

  1. JDK动态代理:适用于那些实现了接口的对象。它通过反射机制在运行时创建代理对象,将横切逻辑织入到这些对象中。
  2. CGLIB代理:适用于没有实现接口的对象。它通过底层的字节码技术生成被代理对象的子类,并在子类中注入横切逻辑。

动态代理

介绍一下相关的功能

在 Java AOP (面向切面编程)中,动态代理是一种实现机制,它在运行时创建一个实现了一组接口的代理对象。这个代理对象可以在原有对象方法调用前后执行额外的操作(如日志记录、事务管理等),而无需修改原有对象的代码。这种机制主要通过两种方式实现:基于接口的 JDK 动态代理和基于类的代理(例如使用 CGLIB 或 ByteBuddy)。

JDK 动态代理

JDK 动态代理使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。这种代理方式要求目标对象必须实现一个或多个接口。

工作原理

  1. 接口实现:动态代理类必须实现目标类的一个或多个接口。
  2. 创建代理实例:通过 Proxy.newProxyInstance 方法创建代理实例。这个方法需要:
    • 类加载器(用于定义代理类)
    • 一组接口(代理类将实现的接口)
    • InvocationHandler 实现(所有方法调用都会转发到这个调用处理器)
  3. 方法调用:当代理实例的方法被调用时,调用会被转发到 InvocationHandlerinvoke 方法。在 invoke 方法中,可以在调用目标方法之前或之后执行其他操作。

通过 point.getTarget() 获取目标实例

在 AOP 框架中,当方法执行被拦截(如通过切点指定的方法),AOP 框架创建一个 JoinPoint 实例,该实例封装了有关当前方法调用的信息。JoinPoint 提供了 getTarget() 方法,该方法返回被代理的目标对象(原始对象),即使当前执行的是代理方法。因此,即使我们操作的是代理实例,我们也可以通过 getTarget() 访问到原始的目标类实例。

CGLIB 动态代理

CGLIB(Code Generation Library)是另一种动态代理方式,它不需要接口。CGLIB 通过继承目标类并在运行时生成子类来实现代理。

工作原理

  1. 子类生成:CGLIB 在运行时生成目标类的子类,并覆盖其方法以提供增强的功能。
  2. 方法调用:当调用代理实例的方法时,实际上是调用由 CGLIB 生成的子类中的方法。在这些方法中,可以在调用实际的目标方法前后插入自定义的行为。

使用场景与选择

  • JDK 动态代理适用于目标对象实现了接口的情况。由于只需代理接口方法,这种方式不会代理类的属性,通常性能较好。
  • CGLIB 动态代理适用于目标对象没有实现任何接口的情况。它通过生成子类来实现代理,更为强大但通常比 JDK 动态代理略慢。

总的来说,AOP 通过动态代理技术提供了一个非常强大的机制,允许开发者在不修改原始代码的情况下,增加额外的功能。通过 point.getTarget() 获取到的是代理调用中的原始对象,确保了即使在代理环境中,也能正确地处理业务逻辑。


参考